/*---------------------------------------------------------------------
 * File name: hal_flash.c
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "stm32g4xx.h"
#include "hal_flash.h"
#include "hal_system.h"
#include "hal_config.h"
/*
*/
static uint32_t GetPage(uint32_t Addr)
{
  uint32_t page = 0;

  if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
  {
    /* Bank 1 */
    page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
  }
  else
  {
    /* Bank 2 */
    page = (Addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
  }

  return page;
}
/*
*/
static uint32_t GetBank(uint32_t Addr)
{
  return FLASH_BANK_1;
}

/*
*/
void HAL_Flash::unlock()
{  
  HAL_FLASH_Unlock(); 
}
/*
*/
void HAL_Flash::lock()
{  
  HAL_FLASH_Lock(); 
}
/*
*/
bool HAL_Flash::erase(uint32_t addr, int size)
{  
  bool ret = false;
  
  uint32_t sr = enter_critical();
  /* Clear OPTVERR bit set on virgin samples */
  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);

  /* Get the 1st page to erase */
  uint32_t first_page = GetPage(addr);

  /* Get the number of pages to erase from 1st page */
  uint32_t page_number = GetPage(addr + size - 1) - first_page + 1;

  /* Get the bank */
  uint32_t bank_number = GetBank(addr);
  
  FLASH_EraseInitTypeDef EraseInitStruct;
  /* Fill EraseInit structure*/
  EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
  EraseInitStruct.Banks       = bank_number;
  EraseInitStruct.Page        = first_page;
  EraseInitStruct.NbPages     = page_number;

  /* Note: If an erase operation in Flash memory also concerns data in the data or instruction cache,
     you have to make sure that these data are rewritten before they are accessed during code
     execution. If this cannot be done safely, it is recommended to flush the caches by setting the
     DCRST and ICRST bits in the FLASH_CR register. */
  uint32_t page_error = 0;
  if (HAL_FLASHEx_Erase(&EraseInitStruct, &page_error) == HAL_OK)
  {
    ret = true;
  }
  exit_critical(sr);
  
  return ret;
}
/*
*/
int HAL_Flash::write(uint32_t addr, uint32_t *data, int num)
{  
  uint32_t sr = enter_critical();
  int num64 = (num + 1)/2;
  for(int i = 0; i<num64; i++)
  {
    int j=i*2;
    uint64_t data64 = (uint64_t)data[j] + ((uint64_t)data[j+1] << 32);
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, data64) == HAL_OK) {
      addr += 8;  /* increment to next double word*/
    } else {
      exit_critical(sr);
      return j;
    }
  }
  exit_critical(sr);
  return num;
}

/*
read
*/
int HAL_Flash::read(uint32_t addr, uint32_t *data, int num)
{
  /* must 4Bytes align */
  if((addr%4) != 0) {return 0;}
  
  uint32_t sr = enter_critical();
  
  for(int i=0; i<num; i++)
  {
    data[i] = *(__IO uint32_t*)(addr + i*4);
  }
  exit_critical(sr);
  return num;
}






